﻿package
{	
	import flash.display.*;
	import flash.events.*;
	import flash.geom.Point;
	import flash.ui.Keyboard;
	import flash.ui.Mouse;
	import flash.utils.getQualifiedClassName;
	import flash.xml.*;
	
	/** Object allows user to draw on a canvas in either of two major modes: Free and Connected.
	Free mode: pen draws as user pressed and holds, fills up pixels underneath pen, eraser does same
	Connected mode: pen connects from a highlighted point, click off points to draw new point (if allowed)
	*/
	public class DrawingPanel extends MovieClip
	{
		//public static const BACK_COLOR:uint = 0xF6F6FF;
		//public static const OUTLINE_COLOR:uint = 0x8888FF;
		public static const BACK_COLOR:uint = 0xE0FFD0;
		public static const OUTLINE_COLOR:uint = 0x88FF88;
		public static const PAINT_COLOR:uint = 0x000000;
		public static var MINIMUM_SEGMENT_LENGTH:int;
		public var WIDTH:Number, HEIGHT:Number;
		public var min_area:Number, parallel_threshold_degrees:Number, length_threshold_pixels:Number, angle_threshold_degrees:Number, right_threshold_degrees:Number;
		public var tool_threshold_multiplier:Number, parallel_threshold_multiplier:Number, length_threshold_multiplier:Number, angle_threshold_multiplier:Number, right_threshold_multiplier:Number;
		public function get modeName():String{return _modeName;}
		// private objects
		private var xml:XML;
		private var undoStack:Array, redoStack:Array;
		private var oobshapes:Array, drawPoints:Array, drawSegments:Array, drawAngles:Array, closedPolygons:Array, childPolygons:Array;
		private var cursor_tool:CursorTool = null;
		private var currentClosedPolygon:ClosedPolygon;
		private var currentChildPolygon:ChildPolygon = null;
		private var currentChildShapeType:String = "";
		// private vars
		private var disconnectedpoints:Boolean, dragpoints:Boolean, outOfBounds:Boolean = false, outOfPanel:Boolean = false;
		private var pointCount:uint = 0, segmentCount:uint = 0, angleCount:uint = 0; //a monotonically increasing count of points used to create a unique id
		private var pen_mode:Boolean = false, drag_mode:Boolean = false, delete_mode:Boolean = false, pin_mode:Boolean = false, polygonSelection_mode:Boolean = false, length_mode:Boolean=false,angle_mode:Boolean=false,right_mode:Boolean=false, parallel_mode:Boolean = false;
		private var _modeName:String = "";
		private var currentDrawState:int = 0;
		private var congruentSegmentCount:uint=0, congruentAngleCount:uint=0, parallelCount:uint=0;
		
		/** WIDTH and HEIGHT obvious, xml provides information about existing points, connectedMode connects points, 
		allowDisconnectedPoints allows users to click a new point of main structure to create a second structure */
		public function DrawingPanel (WIDTH:Number, HEIGHT:Number, xml:XML, connectedMode:Boolean=true, allowDisconnectedPoints:Boolean=false)
		{
			this.WIDTH = WIDTH;
			this.HEIGHT = HEIGHT;
			this.xml = xml;
			this.disconnectedpoints = true;
			this.dragpoints = true;
						
			if (xml.attribute("parallel_threshold_degrees").length()>0){parallel_threshold_degrees = Number(xml.attribute("parallel_threshold_degrees")[0]);}
			else {parallel_threshold_degrees = 1;}
			
			if (xml.attribute("length_threshold_pixels").length()>0){length_threshold_pixels = Number(xml.attribute("length_threshold_pixels")[0]);}
			else {length_threshold_pixels = 1;}
			
			if (xml.attribute("angle_threshold_degrees").length()>0){angle_threshold_degrees = Number(xml.attribute("angle_threshold_degrees")[0]);}
			else {angle_threshold_degrees = 1;}
			
			if (xml.attribute("right_threshold_degrees").length()>0){right_threshold_degrees = Number(xml.attribute("right_threshold_degrees")[0]);}
			else {right_threshold_degrees = 1;}
			
			if (xml.attribute("tool_threshold_multiplier").length()>0){tool_threshold_multiplier = Number(xml.attribute("tool_threshold_multiplier")[0]);}
			else {tool_threshold_multiplier = 20;}			
			
			if (xml.attribute("parallel_threshold_multiplier").length()>0){parallel_threshold_multiplier = Number(xml.attribute("parallel_threshold_multiplier")[0]);}
			else {parallel_threshold_multiplier = tool_threshold_multiplier;}			
			
			if (xml.attribute("length_threshold_multiplier").length()>0){length_threshold_multiplier = Number(xml.attribute("length_threshold_multiplier")[0]);}
			else {length_threshold_multiplier = tool_threshold_multiplier;}			
			
			if (xml.attribute("angle_threshold_multiplier").length()>0){angle_threshold_multiplier = Number(xml.attribute("angle_threshold_multiplier")[0]);}
			else {angle_threshold_multiplier = tool_threshold_multiplier;}			
			
			if (xml.attribute("right_threshold_multiplier").length()>0){right_threshold_multiplier = Number(xml.attribute("right_threshold_multiplier")[0]);}
			else {right_threshold_multiplier = tool_threshold_multiplier;}			
			
			if (xml.attribute("min_area").length()>0){min_area = Number(xml.attribute("min_area")[0]);} 
			else {min_area = 0;}
			if (min_area < 1) {min_area = min_area*this.WIDTH * this.HEIGHT}			
						
			MINIMUM_SEGMENT_LENGTH = ShapeProductionTask.unit*DrawPoint.POINT_SIZE;
			
			// save initial drawstate in stack
			undoStack = new Array();
			redoStack = new Array();
			if (xml..drawstate.length()>0)
			{
				for each (var ds:XML in xml..drawstate)
				{
					undoStack.push(ds);
				}
			} else
			{
				var node:XML = new XML();
				node = <drawstate />
				undoStack.push(node);
			}
				
			
			this.addEventListener(Event.ADDED, handleAdded);
		}
		private function handleAdded(event:Event):void
		{
			this.removeEventListener(Event.ADDED, handleAdded);
			// Give this a light blue hue 
			this.graphics.lineStyle(1, OUTLINE_COLOR);
			this.graphics.beginFill(BACK_COLOR);
			this.graphics.drawRect(0, 0, WIDTH, HEIGHT);
			this.graphics.endFill();
			oobshapes = new Array();
			drawPoints = new Array();
			drawSegments = new Array();
			drawAngles = new Array();
			closedPolygons = new Array();
			childPolygons = new Array();
			// add after the rest of the stuff so that cursor is on top
			// what's the last draw state?
			//totalDrawStates = xml..drawstate.length();
			//currentDrawState = totalDrawStates-1;
			
			drawOobShapesFromXML();
			drawPointsFromXML(undoStack[undoStack.length-1]);
			drawSegmentsFromXML(undoStack[undoStack.length-1]);
			findAllClosedPolygons ();
			
			this.addEventListener(Event.ENTER_FRAME, handleFrame); // does a lot of stuff
			
		}
		/** Iterate through list of out of bound shapes, and their points to create shapes on which a point may not be placed */
		public function drawOobShapesFromXML():void
		{
			//trace(xml..oob.length());
			if (xml..oob.length() > 0)
			{
				for each (var node:XML in xml..oob[0]..oobshape)
				{
					// only bother if we have more than two points in this shape
					if (node..oobpoint.length() >= 3)
					{
						var _points:Array = new Array();
						for each (var pxml:XML in node..oobpoint)
						{
							if (pxml.attribute("x").length() > 0){var px:Number = pxml.attribute("x")[0];}
							else { break; }
							if (px < 1) px = px * WIDTH;
							if (pxml.attribute("y").length() > 0){var py:Number = pxml.attribute("y")[0];}
							if (py < 1) py = py * HEIGHT;
							else { break; }
							var p:Point = new Point(px, py);
							_points.push(p);
						}
						var oobshape:OobShape = new OobShape (_points);
						oobshapes.push(oobshape);
						this.addChild(oobshape);
					}
				}
			}
		}
		/** Iterate through list of initial points in xml doc */
		public function drawPointsFromXML(drawstateNode:XML):void
		{			
			if (drawstateNode != null)
			{
				if (drawstateNode..point.length()>0)
				{
					for each (var node:XML in drawstateNode..point)
					{
						var pid:String="";
						if ( node.attribute("id").length() > 0 ) pid = node.attribute("id")[0];
						var pinned:Boolean=false, fixed:Boolean=false, auto_ppinned:Boolean=true;
						// can this point be deleted?
						if ( node.attribute("pinned").length() > 0 && String(node.attribute("pinned")[0])=="true") pinned = true;
						if ( node.attribute("fixed").length() > 0 && String(node.attribute("fixed")[0])=="true") fixed = true;
						if ( node.attribute("ppinned").length() > 0 && String(node.attribute("ppinned")[0])=="false") auto_ppinned = false;
						var px:Number = Number(node.attribute("x")[0]);
						var py:Number = Number(node.attribute("y")[0]);
						
						if (true)
						{ // draw a point place on panel directly
							var p:DrawPoint = newPoint(pid, pinned, fixed, auto_ppinned);
							if (px >= 1 || py >= 1){ p.x = px; } else {p.x = this.WIDTH * px}
							if (py >= 1 || py >= 1){ p.y = py; } else {p.y = this.HEIGHT * py}
						} 
					}		
				}
			}
		}
		/** Iterate through list of initial segments in xml doc */
		public function drawSegmentsFromXML(drawstateNode:XML):void
		{
			if (drawstateNode != null)
			{
				if (drawstateNode..segment.length() > 0)
				{
					for each (var node:XML in drawstateNode..segment)
					{
						var sid:String="";
						if ( node.attribute("id").length() > 0 ) sid = node.attribute("id")[0];
						
						var p1:DrawPoint = lookupPointByID(node.attribute("p1")[0]);
						var p2:DrawPoint = lookupPointByID(node.attribute("p2")[0]);
						if (p1 != null && p2 != null)
						{
							newSegment(p1, p2, sid);
						} else
						{
							trace("In constructing segment, point was not found, check xml");
						}
					}		
				}
			}
		}
				
		/** Go through array of points to find one that matches the given id */
		private function lookupPointByID(pid:String):DrawPoint
		{
			for each (var p:DrawPoint in drawPoints)
			{
				if (p.id == pid) return p;
			}
			return null;
		}
		
		/////////////////////////////////////////////////////////// FUNCTIONS RELATING TO COMPONENTS (POINTS, SEGMENTS, POLYGONS)/////////////////////////
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/** Removes all points drawAngles and segments from the field */
		public function clear():void
		{
			for (var i:int=drawPoints.length-1; i >= 0; i--)
			{
				var p:DrawPoint = drawPoints[i];
				deletePoint(p);
			}
			this.clearAllClosedPolygons();
		}
		//////////////////////////// POINTS /////////////////////////////////////////////////		

		/** Create a new point with a unique id or given id
		 */ 
		public function newPoint(id:String="no", pinned:Boolean = false, fixed:Boolean = false, auto_ppinned:Boolean=true):DrawPoint
		{
			// update point count
			pointCount++;
			
			var pid:String;
			if (id != "no")
			{ // use given pid
				pid = id;
			} else
			{ // search for a unique name
				pid = String("p"+ pointCount)
			}
			var p:DrawPoint = new DrawPoint(pid, pinned, fixed, auto_ppinned);
			drawPoints.push(p);
			if (cursor_tool == null)
			{
				this.addChild(p);	
			} else
			{
				this.addChildAt(p,this.numChildren-1);	
			}
			//findAllClosedPolygons();		
			return p;
		}
	
		/** Delete point deletes points and all associated segments and angles */
		public function deletePoint(p:DrawPoint):void
		{
			if (p != null && p.parent == this)
			{
				var s:DrawSegment;
				// first get rid of any segments that are attached to this point
				for (var i:int=drawSegments.length-1; i >= 0; i--)
				{
					s = drawSegments[i];
					//trace("segment", i,":", s.containsDrawPoint(p));
					if (s.containsDrawPoint(p))
					{
						// also get rid of segment reference in other points
						s.point2.removeSegment(s);
						s.point1.removeSegment(s);
						drawSegments.splice(i,1);
						this.removeChild(s);
					}
				}
				
				var a:DrawAngle;
				// then angles
				for (i=drawAngles.length-1; i >= 0; i--)
				{
					a = drawAngles[i]
					if (a.containsDrawPoint(p))
					{
						// also get rid of segment reference in other points
						a.point2.removeAngle(a);
						a.point1.removeAngle(a);
						a.pointC.removeAngle(a);
						drawAngles.splice(i,1);
						this.removeChild(a);
					}
				}
				
				drawPoints.splice(drawPoints.indexOf(p),1);
				this.removeChild(p);
				//findAllClosedPolygons();
			}
		}
		/** This function is used to determine if a point is a valid stopping point for a DrawPoint */
		public function isValidPoint (_x:Number, _y:Number):Boolean
		{
			if (_x > 0 && _x < this.WIDTH && _y > 0 && _y < this.HEIGHT)
			{
				var _outOfBounds:Boolean = true;
				// check to see if this point is in a shape
				var gpoint:Point = new Point(_x, _y);
				gpoint = this.localToGlobal(gpoint);
				for each (var oobshape:OobShape in oobshapes)
				{
					if (oobshape.hitTestPoint(gpoint.x, gpoint.y, true)) return false;
				}
				return true;
			} else
			{
				return false;				
			}
		}
		//////////////////////////// SEGMENTS /////////////////////////////////////////////////		

		/** Will create a new segment between two points, but also ensures that the segment is unique */
		public function newSegment(p1:DrawPoint, p2:DrawPoint, id:String = "no", overrideUniquenessCheck:Boolean = false):DrawSegment
		{
			var s:DrawSegment;
			// Make sure this segment is not already created (i.e., has both points)
			var isNewSegment:Boolean = true;
			if (!overrideUniquenessCheck)
			{
				for each (s in drawSegments)
				{
					if (s.containsDrawPoint(p1) && s.containsDrawPoint(p2))
					{
						isNewSegment = false; break;
					}
				}
			}
			if (isNewSegment)
			{
				segmentCount++;
				var sid:String;
				if (id != "no")
				{ // use given pid
					sid = id;
				} else
				{ // search for a unique name
					sid = String("s"+ segmentCount)
				}
				s = new DrawSegment(p1, p2, sid);				
				drawSegments.push(s);
				this.addChildAt(s, oobshapes.length + closedPolygons.length);
				// if this segment has any adjacent segments, i.e., segments attached to this segments point1 or point2, create drawAngles
				var _s:DrawSegment;
				var a:DrawAngle;
				if (p1.segmentcount > 1)
				{
					for each (_s in p1.drawSegments)
					{
						if (s != _s)
						{
							a = new DrawAngle(s, _s);
							//a.drawArc(20);
							this.addChildAt(a, drawSegments.length + oobshapes.length + closedPolygons.length);
							drawAngles.push(a);
						}
					}
				}
				if (p2.segmentcount > 1)
				{
					for each (_s in p2.drawSegments)
					{
						if (s != _s)
						{
							a = new DrawAngle(s, _s);
							//a.drawArc(20);
							this.addChildAt(a, drawSegments.length + oobshapes.length + closedPolygons.length);
							drawAngles.push(a);
						}
					}
				}
			}
			
			//findAllClosedPolygons();
			return(s);
		}
		/** Deletes given point, makes sure that any angles are updated, and references within drawpoints as well */
		public function deleteSegment (ds:DrawSegment, overridePinRequirement:Boolean=false):Boolean
		{
			// if this segment is suspended between two pinned points, cannot delete, send message
			if (!overridePinRequirement && ds.fixed)
			{
				GeometryGameItem(parent).newMessage(this, "You cannot delete this segment.", mouseX, mouseY, MessageBox.EPHEMERAL_TYPE);
				return false;
			}
				
			var angs:Array = new Array();
			// get any angles that include this segment
			for (var i:int=drawAngles.length-1; i >= 0; i--)
			{
				var da:DrawAngle = drawAngles[i];
				if (da.containsDrawSegment(ds))
				{
					// add to angs array
					angs.push(da);
					// remove from list and display
					drawAngles.splice(i, 1);
					this.removeChild(da);					
				}
			}
			// go through all points and remove any references to lost angles and this segment
			for each (var dp:DrawPoint in drawPoints)
			{
				// segment
				dp.removeSegment(ds);
				for each (da in angs)
				{
					dp.removeAngle(da);
				}
			}
			// remove segment from display and list
			drawSegments.splice(drawSegments.indexOf(ds),1);
			this.removeChild(ds);
			
			return true;
		}
		//////////////////////////// CLOSED POLYGONS /////////////////////////////////////////////////		

		/** Function finds all closed polygons on the panel by iterating through all points and segements. Adds to array. Returns number of closed polygons. */
		public function findAllClosedPolygons():void
		{
			clearAllClosedPolygons ();
			// iterate through all points
			for (var i:int=0; i < drawPoints.length; i++)
			{
				var dp:DrawPoint = drawPoints[i];
				// use recursive function to follow tree of all segments
				findAllClosedPolygonsFromDrawPoint(new Array(dp));
			}
		}
		
			/** A recursive function that searches all (DrawSegment) connections to the given DrawPoint (wrapped in an array)
			 * Adds all new ClosedPolygons to array. Returns if any closed polygons found. */
			public function findAllClosedPolygonsFromDrawPoint(prevDrawPoints:Array):Boolean
			{
				//trace("find polygons", drawPoints.length, drawSegments.length, prevDrawPoints);
				var newDrawPoints:Array = null
				var rootdp:DrawPoint = prevDrawPoints[0];
				var dp:DrawPoint = prevDrawPoints[prevDrawPoints.length-1];
				
				var cur_ds:DrawSegment;
				var tdp:DrawPoint, tds:DrawSegment;
				
				// get the final drawsegment connecting the last two drawsegments
				if (prevDrawPoints.length > 1)
				{
					tdp = prevDrawPoints[prevDrawPoints.length-2];
					for each (tds in dp.drawSegments)
					{
						if (tds.containsDrawPoint(tdp))
						{
							cur_ds = tds;
							break;
						}
					}
				}
				
				//trace("this point id", dp.id,  "num segs", dp.drawSegments.length);
				//trace("current draw point", dp.id, dp.drawSegments.length);
				//if (dp.drawSegments.length==2) trace(dp.drawSegments[0].id,dp.drawSegments[1].id); 
				for (var i:int=0; i < dp.drawSegments.length; i++)
				{
					var newpoint:DrawPoint = null;
					var ds:DrawSegment = dp.drawSegments[i];
					if (ds != cur_ds)
					{
					//	trace("iterate segments", ds.id);
						// two cases:
						// 1) This is the first call, we cannot close based on one segment
						if (prevDrawPoints.length == 1)
						{
						//	trace("1 point");
							// which of the two points is the newpoint?
							if (ds.point1 == dp && ds.point2 != dp){ newpoint = ds.point2;}
							else if (ds.point1 != dp && ds.point2 == dp){ newpoint = ds.point1;}
							if (newpoint != null)
							{
								newDrawPoints = prevDrawPoints.slice();
								newDrawPoints.push(newpoint);
								//trace("1", newDrawPoints);
								findAllClosedPolygonsFromDrawPoint(newDrawPoints);
							}
						} // 2) We are one segment removed from the root, we cannot link back to it yet, make sure new point is not root point
						else if (prevDrawPoints.length == 2)
						{
						//	trace("2 points");
							// which of the two points is the newpoint?
							//trace(dp.id, ds.point1.id, ds.point2.id, rootdp);
							if (ds.point1 == dp && ds.point2 != dp && ds.point2 != rootdp){ newpoint = ds.point2;}
							else if (ds.point1 != dp && ds.point2 == dp && ds.point1 != rootdp){ newpoint = ds.point1;}
							if (newpoint != null)
							{
								newDrawPoints = prevDrawPoints.slice();
								newDrawPoints.push(newpoint);
								//trace("2", newDrawPoints);
								findAllClosedPolygonsFromDrawPoint(newDrawPoints);
							} 
						}
						// 3) We have at least two removed, we can connect back to the root point and construct a new closed polygon
						else if (prevDrawPoints.length >= 3)
						{
						//	trace(prevDrawPoints.length, "points");
							var j:uint;
							var p1:DrawPoint, p2:DrawPoint;
							var intersect:Boolean = false;
							// which of the two points is the newpoint?
							if (ds.point1 == dp && ds.point2 != dp){ newpoint = ds.point2;}
							else if (ds.point1 != dp && ds.point2 == dp){ newpoint = ds.point1;}
							if (newpoint == rootdp) 
							{ 
								//trace("return to start");
								// make sure that all segments from 1 to n-1 do no intersect
								for (j=1; j < prevDrawPoints.length-2; j++)
								{
									p1 = prevDrawPoints[j];
									p2 = prevDrawPoints[j+1];
									
									// find the drawsegment between these two
									for each (tds in p1.drawSegments)
									{
										if(tds.containsDrawPoint(p2))
										{
											//trace("last segment", ds.id, "compare to", tds.id, ds==tds, tds.hitTestObject(ds), tds.intersectsDrawSegment(ds));
											//if ( tds.hitTestObject(ds))
											if (tds.intersectsDrawSegment(ds))
											{
												intersect = true;
												break;
											}	
										}
									}
								}
								
								if (!intersect)
								{
						//			trace("create here");
									newDrawPoints = prevDrawPoints.slice();
									return(newClosedPolygon(newDrawPoints));
								}
							}						
							else // new point is not a return to root point, but have we seen it before, or does it intersect with a previous?, if not continue the traverse
							{	// skip root and current point
								var found:Boolean = false;
								for (j=1; j < prevDrawPoints.length-1; j++)	
								{
									var prevdp:DrawPoint = prevDrawPoints[j];
									if (prevdp == newpoint)
									{
										found = true;
										break;
									}
								}
								
								// make sure that all segments from 1 to n-1 do no intersect
								for (j=0; j < prevDrawPoints.length-2; j++)
								{
									p1 = prevDrawPoints[j];
									p2 = prevDrawPoints[j+1];
						//			trace("previous", p1.id, p2.id)
									// find the drawsegment between these two
									for each (tds in p1.drawSegments)
									{
							//			trace("draw segment", tds.id);
										if(tds.containsDrawPoint(p2))
										{
								//			trace("this segment", ds.id, "compare to", tds.id, tds.hitTestObject(ds));
											if ( tds.hitTestObject(ds))
											{
												intersect = true;
												break;
											}	
										}
									}
								}
								if (!found && !intersect)
								{
									newDrawPoints = prevDrawPoints.slice();
									newDrawPoints.push(newpoint);
									//trace("3+", newDrawPoints);
									findAllClosedPolygonsFromDrawPoint(newDrawPoints);
								}
							}
						}
					}
				}
				return false;
			}
		
		/** Constructs a new closed polygon with a set of drawpoints if those points do not already make one, returns true if new closed polygon is made and added to array */
		public function newClosedPolygon (_drawPoints:Array):Boolean
		{
			//_drawPoints = MathUtilities.orderDrawPointsPolygon(_drawPoints); 
			var _points:Array = DrawPoint.toGeomPoints(_drawPoints);
			// go through all closed polygons and make sure this set of points is new
			var found:Boolean = false;
			for each (var cpoly:ClosedPolygon in closedPolygons)
			{
				if (cpoly.containsEquivalentDrawPoints(_drawPoints))
				{
					found = true;
					break;
				}
			}
			if (!found)
			{
				trace("new closed", _drawPoints);
				var closedPolygon:ClosedPolygon = new ClosedPolygon(_drawPoints);
				closedPolygons.push(closedPolygon);
				this.addChildAt(closedPolygon, oobshapes.length);
				return true;
			} else
			{
				//trace("no");
				return false;
			}
		}
		/** Returns an array of closed polygons in which the given point is a member */
		public function getAllClosedPolygonsWithDrawPoint (dp:DrawPoint):Array
		{
			var arr:Array = new Array();
			for each (var p:ClosedPolygon in closedPolygons)
			{
				if (p.containsDrawPoint(dp)) arr.push(p);
			}
			return arr;
		}
		/** Returns an array of closed polygons in which the given draw segment is a member */
		public function getAllClosedPolygonsWithDrawSegment (ds:DrawSegment):Array
		{
			var arr:Array = new Array();
			for each (var p:ClosedPolygon in closedPolygons)
			{
				if (p.containsDrawSegment(ds)) arr.push(p);
			}
			return arr;
		}
		/** Returns an array of closed polygons in which the given draw angle is a member */
		public function getAllClosedPolygonsWithDrawAngle (da:DrawAngle):Array
		{
			var arr:Array = new Array();
			for each (var p:ClosedPolygon in closedPolygons)
			{
				if (p.containsDrawAngle(da)) arr.push(p);
			}
			return arr;
		}
		/** This function searches for all closed polygons with the given draw point, if found, removed from list and display 
		 *  Returns the number of polygons removed;
		 * */
		public function removeClosedPolygonWithDrawPoint(dp:DrawPoint):uint
		{
			var count:uint = 0;
			for (var i:int=closedPolygons.length-1; i >= 0; i--)
			{
				var p:ClosedPolygon = closedPolygons[i];
				
				if (p.containsDrawPoint(dp))
				{
					count++;
					closedPolygons.splice(i, 1);
					if (p.parent == this) this.removeChild(p);
				}
			}
			return count;
		}
		/** Removes all closed polygons from display and off array */
		public function clearAllClosedPolygons ():void
		{
			trace("remove", closedPolygons.length, "closed polygons");
			for each (var p:ClosedPolygon in closedPolygons)
			{
				if (p.parent == this){ this.removeChild(p); trace("remove this closed polygon", p);}
			}
			closedPolygons = new Array();
		}
		
//////////////////////////// CHILD SHAPE GROWTH /////////////////////////////////////////////////		
		
		/** Sets up the growth of a new shape based on the current shape type selected */
		private function newChildPolygon():void
		{
			trace("dpanel, newChildPolygon", currentChildShapeType );
			if (currentClosedPolygon != null && currentChildShapeType != "")
			{
				currentChildPolygon = new ChildPolygon(currentClosedPolygon, currentChildShapeType, min_area);
				//currentChildPolygon.addEventListener(Event.COMPLETE, handleChildSuccess);
				//currentChildPolygon.addEventListener(Event.CANCEL, handleChildFailure);
				this.addChild(currentChildPolygon);
			}
		}
		/** The active child is stored */
		public function storeChild():void
		{
			if (currentChildPolygon != null)
			{
				// make sure this child isn't already on array
				if (childPolygons.indexOf(currentChildPolygon) < 0)
				{
					childPolygons.push(currentChildPolygon);
				}
			}
		}
		public function resetChildren():void
		{
			childPolygons = new Array();
		}
		/** An active child is cleared */
		public function clearChild():void
		{
			if (currentChildPolygon != null)
			{
				//currentChildPolygon.removeEventListener(Event.COMPLETE, handleChildSuccess);
				//currentChildPolygon.removeEventListener(Event.CANCEL, handleChildFailure);
				currentClosedPolygon.outline = false;
				currentClosedPolygon.highlight = false;
				//currentClosedPolygon.used = true;
				currentClosedPolygon.attachedType = this.currentChildShapeType;
				currentClosedPolygon = null;
				//currentChildShapeType = "";
				this.removeChild(currentChildPolygon);
				currentChildPolygon = null;
			}
		}
		
///////////////////////////////// FUNCTIONS FOR TRANSITION /////////////////////////////////////////////		
		
		public function haltCursor():void{cursor_tool.halt = true;}
		public function resumeCursor():void{cursor_tool.halt = false;}
		/** 
		 * Goes through segments and angles to see whether validated properties need to be changed 
		 * */
		public function updateComponents():void
		{
			var arr:Array;
			var dp:DrawPoint, da:DrawAngle, ds:DrawSegment, cp:ClosedPolygon;
			/////////////////////////////////////////// UPDATE CLOSED POLYGONS ////////////////////////////////////////////////////////////
			if (pen_mode)
			{  // i.e., new closed polygon is possible
				if (cursor_tool.components != null)
				{
					if (getQualifiedClassName(cursor_tool.components[0]) == "DrawPoint")
					{
						this.findAllClosedPolygonsFromDrawPoint(new Array(DrawPoint(cursor_tool.components[0])));
					} else if (getQualifiedClassName(cursor_tool.components[0]) == "DrawSegment")
					{
						this.removeClosedPolygonWithDrawPoint(DrawSegment(cursor_tool.components[0]).point1);
						this.removeClosedPolygonWithDrawPoint(DrawSegment(cursor_tool.components[0]).point2);
					} 
				}
			} else if (delete_mode)
			{ // i.e., a closed polygon may be removed
				if (cursor_tool.components != null)
				{
					if (getQualifiedClassName(cursor_tool.components[0]) == "DrawPoint")
					{
						arr = this.getAllClosedPolygonsWithDrawPoint(DrawPoint(cursor_tool.components[0]));
						//trace("delete point", DrawPoint(cursor_tool.components[0]).id, "closed", arr);
						if (arr != null)
						{
							for each (cp in arr)
							{
								cp.childPolygon = null;
								if (cp.attachedType != "")
								{
									GeometryGameItem(parent).processCommand("child", "complete", this, new Array(cp.attachedType));
									cp.attachedType = "";
								}
							}
						}
						this.removeClosedPolygonWithDrawPoint(DrawPoint(cursor_tool.components[0]));
					} else if (getQualifiedClassName(cursor_tool.components[0]) == "DrawSegment")
					{
						arr = this.getAllClosedPolygonsWithDrawSegment(DrawSegment(cursor_tool.components[0]));
						//trace("delete segment", DrawSegment(cursor_tool.components[0]).id, "closed", arr);
						if (arr != null)
						{
							for each (cp in arr)
							{
								cp.childPolygon = null;
								if (cp.attachedType != "")
								{
									GeometryGameItem(parent).processCommand("child", "complete", this, new Array(cp.attachedType));
									cp.attachedType = "";
								}
							}
						}
						this.removeClosedPolygonWithDrawPoint(DrawSegment(cursor_tool.components[0]).point1);
						this.removeClosedPolygonWithDrawPoint(DrawSegment(cursor_tool.components[0]).point2);
					} 
				}
			} else if (drag_mode || parallel_mode || length_mode || angle_mode || right_mode)
			{ // i.e., some close polygons may have changed, if so set polygon's used parameter back to false
				if (drag_mode)
				{
					for each (dp in cursor_tool.components)
					{
						//trace("point changed", dp.id, "remove closed", arr);
						arr = this.getAllClosedPolygonsWithDrawPoint(dp);
						if (arr != null)
						{
							for each (cp in arr)
							{
								cp.childPolygon = null;
								if (cp.attachedType != "")
								{
									GeometryGameItem(parent).processCommand("child", "complete", this, new Array(cp.attachedType));
									cp.attachedType = "";
								}
								// make sure this closed polygon is still a polygon
								if (cp.containsIntersections())
								{
									//trace("number of closed polygson before remove", closedPolygons.length);
									closedPolygons.splice(closedPolygons.indexOf(cp),1);
									if (cp.parent == this) this.removeChild(cp);
									//trace("number of closed polygson after remove", closedPolygons.length);
								} 							
								
							}
						}
						// add on any new polygons made by this drag
						//this.removeClosedPolygonWithDrawPoint(dp);
						//trace("number of closed polygson before add", closedPolygons.length);
						this.findAllClosedPolygonsFromDrawPoint(new Array(dp));
						//trace("number of closed polygson after add", closedPolygons.length);
					}
				} else if (parallel_mode || length_mode)
				{
					for each (ds in cursor_tool.components)
					{
						arr = this.getAllClosedPolygonsWithDrawSegment(ds);
						if (arr != null)
						{
							for each (cp in arr)
							{
								cp.childPolygon = null;
								if (cp.attachedType != "")
								{
									GeometryGameItem(parent).processCommand("child", "complete", this, new Array(cp.attachedType));
									cp.attachedType = "";
								}
								if (cp.containsIntersections())
								{
									closedPolygons.splice(closedPolygons.indexOf(cp),1);
									if (cp.parent == this) this.removeChild(cp);
								} 
							}
						}
						this.findAllClosedPolygonsFromDrawPoint(new Array(ds.point1));
						this.findAllClosedPolygonsFromDrawPoint(new Array(ds.point2));
					}
				} else if (angle_mode || right_mode)
				{
					for each (da in cursor_tool.components)
					{
						arr = this.getAllClosedPolygonsWithDrawAngle(da);
						if (arr != null)
						{
							for each (cp in arr)
							{
								cp.childPolygon = null;
								if (cp.attachedType != "")
								{
									GeometryGameItem(parent).processCommand("child", "complete", this, new Array(cp.attachedType));
									cp.attachedType = "";
								}
								if (cp.containsIntersections())
								{
									closedPolygons.splice(closedPolygons.indexOf(cp),1);
									if (cp.parent == this) this.removeChild(cp);									
								} 
							}
						}
						this.findAllClosedPolygonsFromDrawPoint(new Array(da.point1));
						this.findAllClosedPolygonsFromDrawPoint(new Array(da.point2));
						this.findAllClosedPolygonsFromDrawPoint(new Array(da.pointC));
					}
				}
			}
			
			// redraw all closed polygons
			trace(closedPolygons.length, "closed polygons");
			for each (cp in closedPolygons){cp.redraw();}
			/////////////////////////////////////////// UPDATE CONGRUENCY, PARALLEL, RIGHT ANGLE RELATIONSHIPS ////////////////////////////
			//cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
			if (parallel_mode)
			{
				arr = cursor_tool.components;
				if (arr != null && arr.length > 1) 
				{
					parallelCount++;	
					DrawSegment(arr[0]).addParallelPartner(DrawSegment(arr[1]), parallelCount);
				}
				this.removeChild(cursor_tool);
				cursor_tool = new ParallelTool(drawSegments, oobshapes, tool_threshold_multiplier*parallel_threshold_degrees);
				this.addChild(cursor_tool);
			} else if (length_mode)
			{
				arr = cursor_tool.components;
				// just need to do once
				if (arr != null && arr.length > 1) 
				{
					congruentSegmentCount++;
					DrawSegment(arr[0]).addCongruentPartner(DrawSegment(arr[1]), congruentSegmentCount);
				}
				this.removeChild(cursor_tool);
				cursor_tool = new LengthTool(drawSegments, oobshapes, tool_threshold_multiplier*length_threshold_pixels);
				this.addChild(cursor_tool);
			} else if (angle_mode)
			{
				arr = cursor_tool.components;
				if (arr != null && arr.length > 1)
				{
					congruentAngleCount++;
					congruentAngleCount = DrawAngle(arr[0]).addCongruentPartner(DrawAngle(arr[1]), congruentAngleCount);
				}
				this.removeChild(cursor_tool);
				cursor_tool = new AngleTool(drawAngles, oobshapes, tool_threshold_multiplier*angle_threshold_degrees);
				this.addChild(cursor_tool);
			} else if (right_mode)
			{
				arr = cursor_tool.components;
				if (arr != null && arr.length > 0) DrawAngle(arr[0]).right = true;
				this.removeChild(cursor_tool);
				cursor_tool = new RightTool(drawAngles, oobshapes, tool_threshold_multiplier*angle_threshold_degrees);
				this.addChild(cursor_tool);
			}
			//////////////////////////////////// UPDATE MARKINGS ON SEGMENTS AND ANGLES ////////////////////////////////////////////////////////
			// since we have adjusted something (perhaps) go through all segments and angles and check existing congruencies and parallel lines
			var changed:Boolean = false;
			/////////// congruent sides //////////////////////
			for each (ds in drawSegments)
			{
				if (ds.checkExistingCongruencies(this.length_threshold_pixels)) changed = true;
			}
			// if a change has been made start over with counts, first set counts to zero (which won't affect partner relationships)
			if (changed)
			{
				for each (ds in drawSegments)
				{
					ds.congruentCount = 0;
				}
				congruentSegmentCount=0;
				for each (ds in drawSegments)
				{
					congruentSegmentCount++;
					//trace("congruentSegmentCount", congruentSegmentCount);
					var _count:uint = ds.setMinimumCongruentCount(congruentSegmentCount);
					if (_count > 0) { 	congruentSegmentCount = Math.max(_count, congruentSegmentCount); }
					else { congruentSegmentCount--;} // reduce so that on the next iteration will be increased back to current value
				}
			}
			/////////// parallel sides //////////////////////
			changed = false;
			for each (ds in drawSegments)
			{
				if (ds.checkExistingParallels(this.parallel_threshold_degrees)) changed = true;
			}
			// if a change has been made start over with counts, first set counts to zero (which won't affect partner relationships)
			if (changed)
			{
				for each (ds in drawSegments)
				{
					ds.parallelCount = 0;
				}
				parallelCount=0;
				for each (ds in drawSegments)
				{
					parallelCount++;
					//trace("parallelSegmentCount", parallelSegmentCount);
					_count = ds.setMinimumParallelCount(parallelCount);
					if (_count > 0) { 	parallelCount = Math.max(_count, parallelCount); }
					else { parallelCount--;} // reduce so that on the next iteration will be increased back to current value
				}
			}
			/////////// right angles //////////////////////
			changed = false;
			for each (da in drawAngles)
			{
				if (da.checkExistingRight(this.right_threshold_degrees)) changed = true;
			}
			
			/////////// congruent angles //////////////////////
			changed = false;
			for each (da in drawAngles)
			{
				if (da.checkExistingCongruencies(this.angle_threshold_degrees)) changed = true;
			}
			// if a change has been made start over with counts, first set counts to zero (which won't affect partner relationships)
			if (changed)
			{
				for each (da in drawAngles)
				{
					da.congruentCount = 0;
				}
				congruentAngleCount=0;
				for each (da in drawAngles)
				{
					congruentAngleCount++;
					//trace("congruentAngleCount", congruentAngleCount);
					_count = da.setMinimumCongruentCount(congruentAngleCount);
					if (_count > 0) { 	congruentAngleCount = Math.max(_count, congruentAngleCount); }
					else { congruentAngleCount--;} // reduce so that on the next iteration will be increased back to current value
				}
			}
			
			if (!polygonSelection_mode)
			{
				//findAllClosedPolygons();
			} else
			{
				if (cursor_tool.components != null)
				{
					this.currentClosedPolygon = ClosedPolygon(cursor_tool.components[0]);
					if (currentChildShapeType != "") newChildPolygon();
				}
			}	
		}
		
		/** Make this a separate function from the handler so that it can be called by parent */
		public function completeTool():void
		{
			
			updateComponents();
			pen_mode = false; drag_mode = false; delete_mode = false; pin_mode = false; parallel_mode = false; length_mode = false; angle_mode = false; right_mode = false; polygonSelection_mode = false;
			if (cursor_tool != null)
			{
				//if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
				this.removeChild(cursor_tool); 
				cursor_tool = null;
			}
			//GeometryGameItem(parent).processCommand(modeName, "complete", this);			
		}
		
		public function setChildType(shapeType:String):Boolean
		{
			currentChildShapeType = shapeType;
			//trace("setChildType", currentChildShapeType, currentClosedPolygon);
			//if (currentChildShapeType != "" && currentClosedPolygon != null)
			//{
			//	newChildPolygon();	
			//	return true;
			//} else
			//{
				return false;
			//}
		}
		public function setChildClosedPolygon(closedPolygon:ClosedPolygon):Boolean
		{
			// if we are turning off a current polygon, turn off highlighting and outlining
			if (closedPolygon == null && currentClosedPolygon != null)
			{
				currentClosedPolygon.highlight = false;
			}
			currentClosedPolygon = closedPolygon;
			//trace("setChildClosedPolygon", currentChildShapeType, currentClosedPolygon);
			if (currentChildShapeType != "" && currentClosedPolygon != null)
			{
				newChildPolygon();	
				return true;
			} else 
			{
				return false;
			}
		}
		/** This function changes the type of tool that is being applied on the panel 
		 *	The shapeType parameter is used for the outlining mode.
		 */
		public function shiftModes(_modeName:String, shapeType:String=""):Boolean
		{
			var p:DrawPoint;
			var s:DrawSegment;
			var a:DrawAngle;
			
			this._modeName = _modeName;
			
			switch (modeName)
			{
				case "pen":
					pen_mode = true;
					cursor_tool = new PenTool(drawPoints, drawSegments, this.oobshapes, GeometryGameItem(parent).free_points);
					break;			
				case"drag":
					drag_mode = true;
					cursor_tool = new DragTool(drawPoints, this.oobshapes);
					break;
				case "delete":
					delete_mode = true;
					cursor_tool = new DeleteTool(drawPoints, drawSegments, GeometryGameItem(parent).free_points);
					break;
				case "pin":
					pin_mode = true;
					cursor_tool = new PinTool(drawPoints);
					break;
				case "parallel":
					parallel_mode = true;
					cursor_tool = new ParallelTool(drawSegments, oobshapes, parallel_threshold_multiplier*parallel_threshold_degrees);
					break;
				case "length":
					length_mode = true;
					cursor_tool = new LengthTool(drawSegments, oobshapes, length_threshold_multiplier*length_threshold_pixels);
					break;	
				case "angle":
					angle_mode = true;
					cursor_tool = new AngleTool(drawAngles, oobshapes, angle_threshold_multiplier*angle_threshold_degrees);
					break;
				case "right":
					right_mode = true;
					cursor_tool = new RightTool(drawAngles, oobshapes, right_threshold_multiplier*right_threshold_degrees);
					break;
				case "polygonSelection":
					polygonSelection_mode = true;
					cursor_tool = new PolygonSelectionTool(closedPolygons);
					break;
				case "clear":
					clear();
					break;
				default:
					return false;
			}
			if (cursor_tool != null)
			{
				//cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
				this.addChild(cursor_tool);
			}
			return true;
		}
	
		/** Function for dragging paint or points */
		private function handleFrame (event:Event):void
		{	
			// Are we in bounds?
			if (this.mouseX >= 0 && this.mouseY >= 0 && this.mouseX <= this.WIDTH && this.mouseY <= this.HEIGHT)
			{
				// just coming back in
				if (outOfPanel) {
					outOfPanel = false;
					//trace("cursor_tool", cursor_tool);
					if (cursor_tool != null)
					{
						Mouse.hide();
						cursor_tool.alpha = 1;
					} else
					{
						Mouse.show();
					}
				}
				if (cursor_tool != null) cursor_tool.process(new Point(this.mouseX, this.mouseY));
			} else
			{
				if (!outOfPanel)
				{
					outOfPanel = true;
					Mouse.show();
					if (cursor_tool != null)
					{
						cursor_tool.alpha = 0;
					}
				}
			}
		}
		
////////////////////////////////////////////////////	DEAL WITH STATE OF GAME ///////////////////////////////////////
		/////////////////////////////////////////// 	DO NOT CALL DIRECTLY (CALLED FROM PARENT) ////////////////////////
		/** This function adds a new set of nodes to the xml, at the current level */
		public function appendNewDrawState(saveNode:XML, cmd_count:uint, free_points:int, updateStack:Boolean=true):XML
		{
			currentDrawState++; // append to the end
			// create a node for a new draw state, add points then segments
			var node:XML = new XML();
			node = 
				<drawstate num={currentDrawState}>
				</drawstate>;
			node.@command_count = cmd_count;
			node.@free_points = free_points;
			// go through points to append to node
			
			for (var i:int=0; i < drawPoints.length; i++)
			{
				var p:DrawPoint = drawPoints[i];
				var pnode:XML = new XML();
				pnode = <point />;
				pnode.@id = p.id;
				pnode.@x = String(p.x);
				pnode.@y = String(p.y);
				if (p.pinned) pnode.@pinned = "true";
				if (p.fixed) pnode.@fixed = "true";
				if (!p.ppinned) pnode.@ppinned = "false";
				//<point id=p{p.id} x={p.x} y={p.y} pinned={p.pinned} fixed={p.fixed} />;
				node.appendChild(pnode);
			}
			
			for (i=0; i < drawSegments.length; i++)
			{
				var s:DrawSegment = drawSegments[i];
				var snode:XML = new XML();
				snode = <segment />;
				snode.@id = s.id;
				snode.@p1 = String(s.point1.id);
				snode.@p2 = String(s.point2.id);
				
				//<segment id=s{s.id} x={s.x} y={s.y} />
				node.appendChild(snode);
			}
			if (updateStack)
			{
				undoStack.push(node); // put this on the undo stack
				redoStack = new Array(); // clear the redo stack
			}
			//append to itemxml
			saveNode.appendChild(node);
			return (saveNode);
		}
		
		/** Go through all children, add any that aren't currently saved */
		public function saveChildren():uint
		{
			var saveCount:uint = 0;
			var node:XML = new XML();
			node = 
				<child_shapes>
				</child_shapes>;
			// first is there a node for children
			if (xml..child_shapes.length()==0)
			{
				xml.appendChild(node);
			} else
			{
				xml.replace("child_shapes", node);
				//xml..child_shapes[0].replaceChildren(new XML());	
			}
			//trace(xml);
			
			// look for closed polygons with child references
			for each (var cp:ClosedPolygon in closedPolygons)
			{
				if (cp.used)
				{
					saveCount++;
					// place a text representation of this closedPolygon
					node = new XML();
					node = <child_shape> </child_shape>;
					node.@id = String("c_"+GeometryGameItem(parent).id+"_"+saveCount);
					node.@type = cp.attachedType;
					node.@color = cp.childPolygon.color;
					var pnode:XML;
					var cpoints:Array = MathUtilities.orderPointsPolygon(cp.minimizedPoints);
					//trace("Child points", cp.minimizedPoints);
					for each (var p:Point in cpoints)
					{
						pnode = new XML;
						pnode = <point/>;
						pnode.@x = p.x;
						pnode.@y = p.y;
						node.appendChild(pnode);
					}
					xml.child_shapes[0].appendChild(node);
				}
			}
			//trace(xml);
			//trace(this.parent);
			//ShapeProductionTask(parent.parent.parent.parent).saveXML();
			return saveCount;
		}
		
		///// UN-DOING AND RE-DOING
		public function gotoPreviousState():Boolean
		{
			if (undoStack.length > 0)
			{
				clear();
				//take the last object on the undo stack off, put it on the redo stack, then act upon the new top of the undo stack.
				//currentDrawState--;
				//trace(undoStack.length);
				var node:XML;
				if (undoStack.length > 1) 
				{
					node = undoStack.pop();
					redoStack.push(node);
				}
				//trace(undoStack.length);
				var lastNode:XML = undoStack[undoStack.length-1];
				drawPointsFromXML(lastNode);
				drawSegmentsFromXML(lastNode);
				findAllClosedPolygons();
				//trace(node);
				trace(lastNode);
				//trace(lastNode.attribute("free_points").length());
				if (lastNode.attribute("free_points").length()>0){ GeometryGameItem(parent).free_points = int(lastNode.attribute("free_points")[0]); }
				if (pen_mode){ this.removeChild(cursor_tool); cursor_tool = new PenTool(drawPoints, drawSegments, oobshapes, GeometryGameItem(parent).free_points); this.addChild(cursor_tool);}
				else if (delete_mode){ this.removeChild(cursor_tool); cursor_tool = new DeleteTool(drawPoints, drawSegments, GeometryGameItem(parent).free_points); this.addChild(cursor_tool);}
				
				return true;
			} else
			{
				return false;
			}
		}
		public function gotoFirstState():Boolean
		{
			if (undoStack.length > 0)
			{
				clear();
				//currentDrawState=0;
				// go through all elements on the undo stack and push them onto the redo stack
				for (var i:int=undoStack.length-1; i > 0; i--)
				{
					var node:XML = undoStack.pop();
					redoStack.push(node);
				}
				var lastNode:XML = undoStack[undoStack.length-1];
				drawPointsFromXML(lastNode);
				drawSegmentsFromXML(lastNode);
				findAllClosedPolygons();
				if (lastNode.attribute("free_points").length()>0){ GeometryGameItem(parent).free_points = int(lastNode.attribute("free_points")[0]); }
				if (pen_mode){ this.removeChild(cursor_tool); cursor_tool = new PenTool(drawPoints, drawSegments, oobshapes, GeometryGameItem(parent).free_points); this.addChild(cursor_tool);}
				else if (delete_mode){ this.removeChild(cursor_tool); cursor_tool = new DeleteTool(drawPoints, drawSegments, GeometryGameItem(parent).free_points); this.addChild(cursor_tool);}
				return true;
			} else
			{
				return false;
			}
		}
		public function gotoNextState():Boolean
		{
			if (redoStack.length > 0)
			{
				clear();
				//currentDrawState++;
				// take the last object off the redo stack and place on the undo stack
				var node:XML = redoStack.pop();
				undoStack.push(node);
				var lastNode:XML = undoStack[undoStack.length-1];
				drawPointsFromXML(lastNode);
				drawSegmentsFromXML(lastNode);
				findAllClosedPolygons();
				if (lastNode.attribute("free_points").length()>0){ GeometryGameItem(parent).free_points = int(lastNode.attribute("free_points")[0]);}
				if (pen_mode){ this.removeChild(cursor_tool); cursor_tool = new PenTool(drawPoints, drawSegments, oobshapes, GeometryGameItem(parent).free_points); this.addChild(cursor_tool);}
				else if (delete_mode){ this.removeChild(cursor_tool); cursor_tool = new DeleteTool(drawPoints, drawSegments, GeometryGameItem(parent).free_points); this.addChild(cursor_tool);}
				return true;
			} else
			{
				return false;
			}
		}
		public function gotoLastState():Boolean
		{
			if (redoStack.length > 0)
			{
				clear();
				//currentDrawState = totalDrawStates-1;
				for (var i:int=redoStack.length-1; i > 0; i--)
				{
					var node:XML = redoStack.pop();
					undoStack.push(node);
				}
				var lastNode:XML = undoStack[undoStack.length-1];
				drawPointsFromXML(lastNode);
				drawSegmentsFromXML(lastNode);
				findAllClosedPolygons();
				if (lastNode.attribute("free_points").length()>0){ GeometryGameItem(parent).free_points = int(lastNode.attribute("free_points")[0]); }
				if (pen_mode){ this.removeChild(cursor_tool); cursor_tool = new PenTool(drawPoints, drawSegments, oobshapes, GeometryGameItem(parent).free_points); this.addChild(cursor_tool);}
				else if (delete_mode){ this.removeChild(cursor_tool); cursor_tool = new DeleteTool(drawPoints, drawSegments, GeometryGameItem(parent).free_points); this.addChild(cursor_tool);}
				return true;
			} else
			{
				return false;
			}
		}		
	}	
}